using System;
using System.Configuration;
using System.Threading;
using System.Xml;

namespace ELS.Configuration
{
	#region ConfigurationManager
	/// <summary>
	/// Provides a singleton facade to work with configuration settings. 
	/// </summary>
	public sealed class ConfigurationManager 
	{

		#region variables
		// the framework guarantees thread safety on static type initialization.
		// marked readonly to ensure that it can be assigned only during static initialization 
		private readonly static ConfigurationManager _instance = new ConfigurationManager();
		
		// synclock object to lock on for parallel processing 
		private object _syncLock = new object();

		private ConfigurationCollection _configurations = null;
		#endregion variables

		#region public properties
		/// <summary>
		/// The singleton instance to use with static methods.
		/// </summary>
		public  static ConfigurationManager Instance
		{
			get	{ return _instance;	}
		}
		#endregion public properties

		#region constructors
		/// <summary>
		/// private constructor
		/// </summary>
		private ConfigurationManager()
		{
			_configurations = new ConfigurationCollection();
		}
		#endregion constructors

		#region public methods
		/// <summary>
		/// Returns requested configuration information.
		/// </summary>
		/// <param name="name">The configuration name that information is requested for.</param>
		/// <returns>a configuration object.</returns>
		
		/// <summary>
		/// Gets the collection of configurations.
		/// </summary>
		public ConfigurationCollection Configurations
		{
			get { return _configurations; }
		}


		/// <summary>
		/// Initializes the <see cref="ELS.Configuration.ConfigurationManager"/>
		/// </summary>
		public void Initialize()
		{
			using ( new TimedLock( _syncLock ) )
			{
				const string ERROR_MESSAGE = "No configuration settings found.";
				string configSettingsXml = ConfigurationSettings.GetConfig("els.configurationProviderSettings") as string;
				if ( configSettingsXml == null )
					throw new ApplicationException(ERROR_MESSAGE); 
						
				XmlDocument doc = new XmlDocument();
				doc.LoadXml( configSettingsXml );

				XmlNamespaceManager nsmgr = new XmlNamespaceManager( doc.NameTable );
				nsmgr.AddNamespace( Constants.NAMESPACE_PREFIX, Constants.NAMESPACE_URI );

				string xpath = "//ns:els.configurationProviderSettings/ns:configuration";

				XmlNodeList configNodeList = doc.DocumentElement.SelectNodes(xpath , nsmgr);

				if ( configNodeList == null )
					throw new ApplicationException(ERROR_MESSAGE); 
				
				ConfigurationProvider provider = null;
				foreach( XmlNode configNode in configNodeList )
				{
					XmlNode providerNode = configNode.SelectSingleNode("./ns:provider", nsmgr);
					provider =  ConfigurationProviderFactory.Create( providerNode );
					_configurations = provider.Configurations;
				}

			}
		}

		/// <summary>
		/// Gets the section requested from the config specified.
		/// </summary>
		/// <param name="configName">The configuration name that information is requested for.</param>
		/// <param name="sectionName">The section name that information is requested for.</param>
		/// <returns>a <see cref="ELS.Configuration.ConfigurationSection"/> object.</returns>
		public ConfigurationSection GetSection( string configName, string sectionName )
		{
			Configuration  cfg = _configurations[ configName ];
			return cfg.GetSection( sectionName );
		}


		/// <summary>
		/// Returns requested <see cref="ELS.Configuration.ConfigurationValueCollection"/>.
		/// </summary>
		/// <param name="configName">The configuration name that information is requested for.</param>
		/// <param name="sectionName">The section name that information is requested for.</param>
		/// <param name="propertyName">The property name that information is requested for.</param>
		/// <returns>a <see cref="ELS.Configuration.ConfigurationValueCollection"/> object.</returns>
		public ConfigurationValueCollection GetValue( string configName, string sectionName, string propertyName )
		{
			Configuration  cfg = _configurations [ configName ];
			ConfigurationSection  cfgSection = cfg.GetSection( sectionName );
			return cfgSection.Properties[ propertyName ].Values;
		}
		#endregion public methods
	}

	#endregion ConfigurationManager
}
